// Copyright (c) 2021 Presto Labs Pte. Ltd.
// Author: daniel

#pragma once

#include <memory>
#include <string>
#include <string_view>
#include <unordered_set>
#include <utility>
#include <vector>

#include "coin/proto/coin_order_enums.pb.h"
#include "coin2/exchange/base/api_base/asio_http_context.h"
#include "coin2/exchange/base/order/parser.h"
#include "coin2/exchange/base/order/product_info_cache.h"
#include "coin2/exchange/base/symbology/currency.h"
#include "coin2/exchange/gopax/symbology/currency.h"
#include "coin2/exchange/gopax/symbology/product.h"

namespace coin2::exchange::gopax::order_v1 {

class GopaxParser : public base::order::BaseParser {
  using JsonValue = base::order::BaseParser::JsonValue;
  using SubmitOrderResponse = base::order::BaseParser::SubmitOrderResponse;
  using ProductOrderBundle = base::order::BaseParser::ProductOrderBundle;
  using ProductFillBundle = base::order::BaseParser::ProductFillBundle;
  using AccountBalance = base::order::BaseParser::AccountBalance;
  using ProductOrderElement = base::order::BaseParser::ProductOrderElement;
  using AccountPosition = base::order::BaseParser::AccountPosition;
  using OrderErrorCode = base::order::BaseParser::OrderErrorCode;
  using OrderSubTopicId = base::order::BaseParser::OrderSubTopicId;
  using OrderSide = coin::proto::OrderSide;
  using OrderType = coin::proto::OrderType;
  using CurrencyTransferBundle = coin::proto::CurrencyTransferBundle;

 public:
  const ParseResult& ParseRawMessage(const RawMessage& msg) override;
  // keep this api for accounting system
  AccountBalance ParseBalance(const JsonValue& document) override;
  CurrencyTransferBundle* ParseTransfer(const JsonValue& data);
  bool ParseRawBalance(const JsonValue& doc, RawBalanceMap* raw_balance_map);

 private:
  void ParseSubmitResponse();
  void ParseCancelResponse();
  void ParseRestAllOrders();
  void ParseRestFillInfoList();
  void ParseRestAccountBalance();
  void ParseRestTransfer();
  void ParseWsMessage();

  auto ParseBalanceFromWs(const JsonValue& document) -> AccountBalance;
  auto ParseBalanceFromWsDelta(const JsonValue& document) -> AccountBalance;
  auto ParseRestOrderInfoFromWs(const JsonValue& data) -> ProductOrderBundle;
  auto ParseRestOrderInfoFromWsDelta(const JsonValue& data) -> ProductOrderBundle;
  auto ParseRestFillInfoListFromWsDelta(const JsonValue& data) -> ProductFillBundle;
  void ParseRestOrderInfo(const JsonValue& data, ProductOrderElement* product_order_element);
  bool ParseRestFillInfo(const JsonValue& data, ProductFillBundle* product_fill_bundle);

  bool IsGopaxErrMessage(const JsonValue& data) const;
  OrderSide GetOrderSide(std::string_view side) const;
  OrderSide GetOrderSideFromWs(std::string_view side) const;
  OrderType GetOrderType(std::string_view type) const;
  OrderType GetOrderTypeFromWs(std::string_view type) const;
  void SetOrderStatus(const JsonValue& data, ProductOrderElement* product_order_element) const;
  void SetOrderStatusFromWs(const JsonValue& data, ProductOrderElement* product_order_element)
      const;

  const std::unordered_set<std::string>& native_symbols() {
    static std::unordered_set<std::string> native_symbols = [&]() -> auto {
      std::unordered_set<std::string> set;
      for (const auto* product : symcache_->GetProducts()) {
        set.emplace(product->order_native_symbol());
      }
      return set;
    }
    ();
    return native_symbols;
  }

 private:
  bool is_account_balance_initialized_ = false;
};

}  // namespace coin2::exchange::gopax::order_v1
